Trò chơi Tic-Tac-Toe, game đánh caro full source code
- PickupController.cs
- Scripts /
- DemoPickup /
- Demos /
- Photon Unity Networking /
- Assets /
- project /
2 using System.Collections;
3
4
5 public enum PickupCharacterState
6 {
7 Idle = 0,
8 Walking = 1,
9 Trotting = 2,
10 Running = 3,
11 Jumping = 4,
12 }
13
14 [RequireComponent(typeof(CharacterController))]
15 public class PickupController : MonoBehaviour, IPunObservable
16 {
17
18 public AnimationClip idleAnimation;
19 public AnimationClip walkAnimation;
20 public AnimationClip runAnimation;
21 public AnimationClip jumpPoseAnimation;
22
23 public float walkMaxAnimationSpeed = 0.75f;
24 public float trotMaxAnimationSpeed = 1.0f;
25 public float runMaxAnimationSpeed = 1.0f;
26 public float jumpAnimationSpeed = 1.15f;
27 public float landAnimationSpeed = 1.0f;
28
29 private Animation _animation;
30
31
32
33 public PickupCharacterState _characterState;
34
35 // The speed when walking
36 public float walkSpeed = 2.0f;
37 // after trotAfterSeconds of walking we trot with trotSpeed
38 public float trotSpeed = 4.0f;
39 // when pressing "Fire3" button (cmd) we start running
40 public float runSpeed = 6.0f;
41
42 public float inAirControlAcceleration = 3.0f;
43
44 // How high do we jump when pressing jump and letting go immediately
45 public float jumpHeight = 0.5f;
46
47 // The gravity for the character
48 public float gravity = 20.0f;
49 // The gravity in controlled descent mode
50 public float speedSmoothing = 10.0f;
51 public float rotateSpeed = 500.0f;
52 public float trotAfterSeconds = 3.0f;
53
54 public bool canJump = false;
55
56 private float jumpRepeatTime = 0.05f;
57 private float jumpTimeout = 0.15f;
58 private float groundedTimeout = 0.25f;
59
60 // The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around.
61 private float lockCameraTimer = 0.0f;
62
63 // The current move direction in x-z
64 private Vector3 moveDirection = Vector3.zero;
65 // The current vertical speed
66 private float verticalSpeed = 0.0f;
67 // The current x-z move speed
68 private float moveSpeed = 0.0f;
69
70 // The last collision flags returned from controller.Move
71 private CollisionFlags collisionFlags;
72
73 // Are we jumping? (Initiated with jump button and not grounded yet)
74 private bool jumping = false;
75 private bool jumpingReachedApex = false;
76
77 // Are we moving backwards (This locks the camera to not do a 180 degree spin)
78 private bool movingBack = false;
79 // Is the user pressing any keys?
80 private bool isMoving = false;
81 // When did the user start walking (Used for going into trot after a while)
82 private float walkTimeStart = 0.0f;
83 // Last time the jump button was clicked down
84 private float lastJumpButtonTime = -10.0f;
85 // Last time we performed a jump
86 private float lastJumpTime = -1.0f;
87 // the height we jumped from (Used to determine for how long to apply extra jump power after jumping.)
88 //private float lastJumpStartHeight = 0.0f;
89 private Vector3 inAirVelocity = Vector3.zero;
90
91 private float lastGroundedTime = 0.0f;
92 Vector3 velocity = Vector3.zero;
93 private Vector3 lastPos;
94 private Vector3 remotePosition;
95
96 public bool isControllable = false;
97 public bool DoRotate = true;
98 public float RemoteSmoothing = 5;
99 public bool AssignAsTagObject = true;
100
101 void Awake()
102 {
103 // PUN: automatically determine isControllable, if this GO has a PhotonView
104 PhotonView pv = this.gameObject.GetComponent<PhotonView>();
105 if (pv != null)
106 {
107 isControllable = pv.isMine;
108
109 // The pickup demo assigns this GameObject as the PhotonPlayer.TagObject. This way, we can access this character (controller, position, etc) easily
110 if (this.AssignAsTagObject)
111 {
112 pv.owner.TagObject = this.gameObject;
113 }
114
115 // please note: we change this setting on ANY PickupController if "DoRotate" is off. not only locally when it's "our" GameObject!
116 if (pv.observed is Transform && !DoRotate)
117 {
118 pv.onSerializeTransformOption = OnSerializeTransform.OnlyPosition;
119 }
120 }
121
122
123 moveDirection = transform.TransformDirection(Vector3.forward);
124
125 _animation = GetComponent<Animation>();
126 if (!_animation)
127 Debug.Log("The character you would like to control doesn't have animations. Moving her might look weird.");
128
129 if (!idleAnimation)
130 {
131 _animation = null;
132 Debug.Log("No idle animation found. Turning off animations.");
133 }
134 if (!walkAnimation)
135 {
136 _animation = null;
137 Debug.Log("No walk animation found. Turning off animations.");
138 }
139 if (!runAnimation)
140 {
141 _animation = null;
142 Debug.Log("No run animation found. Turning off animations.");
143 }
144 if (!jumpPoseAnimation && canJump)
145 {
146 _animation = null;
147 Debug.Log("No jump animation found and the character has canJump enabled. Turning off animations.");
148 }
149 }
150
151 void Update()
152 {
153 if (isControllable)
154 {
155 if (Input.GetButtonDown("Jump"))
156 {
157 lastJumpButtonTime = Time.time;
158 }
159
160 UpdateSmoothedMovementDirection();
161
162 // Apply gravity
163 // - extra power jump modifies gravity
164 // - controlledDescent mode modifies gravity
165 ApplyGravity();
166
167 // Apply jumping logic
168 ApplyJumping();
169
170
171 // Calculate actual motion
172 Vector3 movement = moveDirection * moveSpeed + new Vector3(0, verticalSpeed, 0) + inAirVelocity;
173 movement *= Time.deltaTime;
174
175 //Debug.Log(movement.x.ToString("0.000") + ":" + movement.z.ToString("0.000"));
176
177 // Move the controller
178 CharacterController controller = GetComponent<CharacterController>();
179 collisionFlags = controller.Move(movement);
180
181 }
182
183 // PUN: if a remote position is known, we smooth-move to it (being late(r) but smoother)
184 if (this.remotePosition != Vector3.zero)
185 {
186 transform.position = Vector3.Lerp(transform.position, this.remotePosition, Time.deltaTime * this.RemoteSmoothing);
187 }
188
189 velocity = (transform.position - lastPos)*25;
190
191 // ANIMATION sector
192 if (_animation)
193 {
194 if (_characterState == PickupCharacterState.Jumping)
195 {
196 if (!jumpingReachedApex)
197 {
198 _animation[jumpPoseAnimation.name].speed = jumpAnimationSpeed;
199 _animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
200 _animation.CrossFade(jumpPoseAnimation.name);
201 }
202 else
203 {
204 _animation[jumpPoseAnimation.name].speed = -landAnimationSpeed;
205 _animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
206 _animation.CrossFade(jumpPoseAnimation.name);
207 }
208 }
209 else
210 {
211 if (_characterState == PickupCharacterState.Idle)
212 {
213 _animation.CrossFade(idleAnimation.name);
214 }
215 else if (_characterState == PickupCharacterState.Running)
216 {
217 _animation[runAnimation.name].speed = runMaxAnimationSpeed;
218 if (this.isControllable)
219 {
220 _animation[runAnimation.name].speed = Mathf.Clamp(velocity.magnitude, 0.0f, runMaxAnimationSpeed);
221 }
222 _animation.CrossFade(runAnimation.name);
223 }
224 else if (_characterState == PickupCharacterState.Trotting)
225 {
226 _animation[walkAnimation.name].speed = trotMaxAnimationSpeed;
227 if (this.isControllable)
228 {
229 _animation[walkAnimation.name].speed = Mathf.Clamp(velocity.magnitude, 0.0f, trotMaxAnimationSpeed);
230 }
231 _animation.CrossFade(walkAnimation.name);
232 }
233 else if (_characterState == PickupCharacterState.Walking)
234 {
235 _animation[walkAnimation.name].speed = walkMaxAnimationSpeed;
236 if (this.isControllable)
237 {
238 _animation[walkAnimation.name].speed = Mathf.Clamp(velocity.magnitude, 0.0f, walkMaxAnimationSpeed);
239 }
240 _animation.CrossFade(walkAnimation.name);
241 }
242
243 if (_characterState != PickupCharacterState.Running)
244 {
245 _animation[runAnimation.name].time = 0.0f;
246 }
247 }
248 }
249 // ANIMATION sector
250
251 // Set rotation to the move direction
252 if (IsGrounded())
253 {
254 // a specialty of this controller: you can disable rotation!
255 if (DoRotate)
256 {
257 transform.rotation = Quaternion.LookRotation(moveDirection);
258 }
259 }
260 else
261 {
262 /* This causes choppy behaviour when colliding with SIDES
263 * Vector3 xzMove = velocity;
264 xzMove.y = 0;
265 if (xzMove.sqrMagnitude > 0.001f)
266 {
267 transform.rotation = Quaternion.LookRotation(xzMove);
268 }*/
269 }
270
271 // We are in jump mode but just became grounded
272 if (IsGrounded())
273 {
274 lastGroundedTime = Time.time;
275 inAirVelocity = Vector3.zero;
276 if (jumping)
277 {
278 jumping = false;
279 SendMessage("DidLand", SendMessageOptions.DontRequireReceiver);
280 }
281 }
282
283 lastPos = transform.position;
284 }
285
286 public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
287 {
288 if (stream.isWriting)
289 {
290 stream.SendNext(this.transform.position);
291 stream.SendNext((byte)this._characterState);
292 }
293 else
294 {
295 bool initialRemotePosition = (remotePosition == Vector3.zero);
296
297 remotePosition = (Vector3)stream.ReceiveNext();
298 this._characterState = (PickupCharacterState)((byte)stream.ReceiveNext());
299
300 if (initialRemotePosition)
301 {
302 // avoids lerping the character from "center" to the "current" position when this client joins
303 this.transform.position = remotePosition;
304 }
305 }
306 }
307
308 void UpdateSmoothedMovementDirection()
309 {
310 Transform cameraTransform = Camera.main.transform;
311 bool grounded = IsGrounded();
312
313 // Forward vector relative to the camera along the x-z plane
314 Vector3 forward = cameraTransform.TransformDirection(Vector3.forward);
315 forward.y = 0;
316 forward = forward.normalized;
317
318 // Right vector relative to the camera
319 // Always orthogonal to the forward vector
320 Vector3 right = new Vector3(forward.z, 0, -forward.x);
321
322 float v = Input.GetAxisRaw("Vertical");
323 float h = Input.GetAxisRaw("Horizontal");
324
325 // Are we moving backwards or looking backwards
326 if (v < -0.2f)
327 movingBack = true;
328 else
329 movingBack = false;
330
331 bool wasMoving = isMoving;
332 isMoving = Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f;
333
334 // Target direction relative to the camera
335 Vector3 targetDirection = h * right + v * forward;
336 // Debug.Log("targetDirection " + targetDirection);
337
338 // Grounded controls
339 if (grounded)
340 {
341 // Lock camera for short period when transitioning moving & standing still
342 lockCameraTimer += Time.deltaTime;
343 if (isMoving != wasMoving)
344 lockCameraTimer = 0.0f;
345
346 // We store speed and direction seperately,
347 // so that when the character stands still we still have a valid forward direction
348 // moveDirection is always normalized, and we only update it if there is user input.
349 if (targetDirection != Vector3.zero)
350 {
351 // If we are really slow, just snap to the target direction
352 if (moveSpeed < walkSpeed * 0.9f && grounded)
353 {
354 moveDirection = targetDirection.normalized;
355 }
356 // Otherwise smoothly turn towards it
357 else
358 {
359 moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000);
360
361 moveDirection = moveDirection.normalized;
362 }
363 }
364
365 // Smooth the speed based on the current target direction
366 float curSmooth = speedSmoothing * Time.deltaTime;
367
368 // Choose target speed
369 //* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways
370 float targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0f);
371
372 _characterState = PickupCharacterState.Idle;
373
374 // Pick speed modifier
375 if ((Input.GetKey(KeyCode.LeftShift) | Input.GetKey(KeyCode.RightShift)) && isMoving)
376 {
377 targetSpeed *= runSpeed;
378 _characterState = PickupCharacterState.Running;
379 }
380 else if (Time.time - trotAfterSeconds > walkTimeStart)
381 {
382 targetSpeed *= trotSpeed;
383 _characterState = PickupCharacterState.Trotting;
384 }
385 else if (isMoving)
386 {
387 targetSpeed *= walkSpeed;
388 _characterState = PickupCharacterState.Walking;
389 }
390
391 moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);
392
393 // Reset walk time start when we slow down
394 if (moveSpeed < walkSpeed * 0.3f)
395 walkTimeStart = Time.time;
396 }
397 // In air controls
398 else
399 {
400 // Lock camera while in air
401 if (jumping)
402 lockCameraTimer = 0.0f;
403
404 if (isMoving)
405 inAirVelocity += targetDirection.normalized * Time.deltaTime * inAirControlAcceleration;
406 }
407 }
408
409 void ApplyJumping()
410 {
411 // Prevent jumping too fast after each other
412 if (lastJumpTime + jumpRepeatTime > Time.time)
413 return;
414
415 if (IsGrounded())
416 {
417 // Jump
418 // - Only when pressing the button down
419 // - With a timeout so you can press the button slightly before landing
420 if (canJump && Time.time < lastJumpButtonTime + jumpTimeout)
421 {
422 verticalSpeed = CalculateJumpVerticalSpeed(jumpHeight);
423 SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
424 }
425 }
426 }
427
428 void ApplyGravity()
429 {
430 if (isControllable) // don't move player at all if not controllable.
431 {
432 // Apply gravity
433 //bool jumpButton = Input.GetButton("Jump");
434
435 // When we reach the apex of the jump we send out a message
436 if (jumping && !jumpingReachedApex && verticalSpeed <= 0.0f)
437 {
438 jumpingReachedApex = true;
439 SendMessage("DidJumpReachApex", SendMessageOptions.DontRequireReceiver);
440 }
441
442 if (IsGrounded())
443 verticalSpeed = 0.0f;
444 else
445 verticalSpeed -= gravity * Time.deltaTime;
446 }
447 }
448
449 float CalculateJumpVerticalSpeed(float targetJumpHeight)
450 {
451 // From the jump height and gravity we deduce the upwards speed
452 // for the character to reach at the apex.
453 return Mathf.Sqrt(2 * targetJumpHeight * gravity);
454 }
455
456 void DidJump()
457 {
458 jumping = true;
459 jumpingReachedApex = false;
460 lastJumpTime = Time.time;
461 //lastJumpStartHeight = transform.position.y;
462 lastJumpButtonTime = -10;
463
464 _characterState = PickupCharacterState.Jumping;
465 }
466
467 void OnControllerColliderHit(ControllerColliderHit hit)
468 {
469 // Debug.DrawRay(hit.point, hit.normal);
470 if (hit.moveDirection.y > 0.01f)
471 return;
472 }
473
474 public float GetSpeed()
475 {
476 return moveSpeed;
477 }
478
479 public bool IsJumping()
480 {
481 return jumping;
482 }
483
484 public bool IsGrounded()
485 {
486 return (collisionFlags & CollisionFlags.CollidedBelow) != 0;
487 }
488
489 public Vector3 GetDirection()
490 {
491 return moveDirection;
492 }
493
494 public bool IsMovingBackwards()
495 {
496 return movingBack;
497 }
498
499 public float GetLockCameraTimer()
500 {
501 return lockCameraTimer;
502 }
503
504 public bool IsMoving()
505 {
506 return Mathf.Abs(Input.GetAxisRaw("Vertical")) + Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5f;
507 }
508
509 public bool HasJumpReachedApex()
510 {
511 return jumpingReachedApex;
512 }
513
514 public bool IsGroundedWithTimeout()
515 {
516 return lastGroundedTime + groundedTimeout > Time.time;
517 }
518
519 public void Reset()
520 {
521 gameObject.tag = "Player";
522 }
523 }
The speed when walking
after trotAfterSeconds of walking we trot with trotSpeed
when pressing "Fire3" button (cmd) we start running
How high do we jump when pressing jump and letting go immediately
The gravity for the character
The gravity in controlled descent mode
The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around.
The current move direction in x-z
The current vertical speed
The current x-z move speed
The last collision flags returned from controller.Move
Are we jumping? (Initiated with jump button and not grounded yet)
Are we moving backwards (This locks the camera to not do a 180 degree spin)
Is the user pressing any keys?
When did the user start walking (Used for going into trot after a while)
Last time the jump button was clicked down
Last time we performed a jump
the height we jumped from (Used to determine for how long to apply extra jump power after jumping.)
private float lastJumpStartHeight = 0.0f;
PUN: automatically determine isControllable, if this GO has a PhotonView
The pickup demo assigns this GameObject as the PhotonPlayer.TagObject. This way, we can access this character (controller, position, etc) easily
please note: we change this setting on ANY PickupController if "DoRotate" is off. not only locally when it's "our" GameObject!
Apply gravity
- extra power jump modifies gravity
- controlledDescent mode modifies gravity
Apply jumping logic
Calculate actual motion
Debug.Log(movement.x.ToString("0.000") + ":" + movement.z.ToString("0.000"));
Move the controller
PUN: if a remote position is known, we smooth-move to it (being late(r) but smoother)
ANIMATION sector
ANIMATION sector
Set rotation to the move direction
a specialty of this controller: you can disable rotation!
We are in jump mode but just became grounded
avoids lerping the character from "center" to the "current" position when this client joins
Forward vector relative to the camera along the x-z plane
Right vector relative to the camera
Always orthogonal to the forward vector
Are we moving backwards or looking backwards
Target direction relative to the camera
Debug.Log("targetDirection " + targetDirection);
Grounded controls
Lock camera for short period when transitioning moving & standing still
We store speed and direction seperately,
so that when the character stands still we still have a valid forward direction
moveDirection is always normalized, and we only update it if there is user input.
If we are really slow, just snap to the target direction
Otherwise smoothly turn towards it
Smooth the speed based on the current target direction
Choose target speed
* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways
Pick speed modifier
Reset walk time start when we slow down
In air controls
Lock camera while in air
Prevent jumping too fast after each other
Jump
- Only when pressing the button down
- With a timeout so you can press the button slightly before landing
if (isControllable) don't move player at all if not controllable.
Apply gravity
bool jumpButton = Input.GetButton("Jump");
When we reach the apex of the jump we send out a message
From the jump height and gravity we deduce the upwards speed
for the character to reach at the apex.
lastJumpStartHeight = transform.position.y;
Debug.DrawRay(hit.point, hit.normal);